/*
 * Copyright (c) zhg2yqq.com Corp.
 * All Rights Reserved.
 */
package com.zhg2yqq.wheels.common.response;

import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.zhg2yqq.wheels.common.exception.BusinessException;
import com.zhg2yqq.wheels.common.exception.SystemException;
import com.zhg2yqq.wheels.common.util.ServletUtils;

import cn.hutool.core.text.CharSequenceUtil;

/**
 * @author zhg2yqq, 2022年12月4日
 * @version zhg2yqq v1.0
 */
@RestControllerAdvice(annotations = {RestController.class, ResponseBody.class})
@ControllerAdvice(annotations = {Controller.class, ResponseBody.class})
public class RestResponseBodyAdvice extends AbstractAdvice {
    private Logger log = LoggerFactory.getLogger(RestResponseBodyAdvice.class);

    @Autowired
    private MessageSource messageSource;

    @ExceptionHandler({BusinessException.class})
    public RestResponse<Object> businessExceptionHandler(BusinessException e,
                                                         HttpServletRequest request,
                                                         HttpServletResponse response) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        if (!ServletUtils.isAjaxRequest(request)) {
            String message = e.getDefaultMessage();
            try {
                message = messageSource.getMessage(e.getCode(), e.getParameters(),
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(e.getCode(), e.getDefaultMessage())
                .formateLocalMessage(messageSource, e.getParameters());
    }

    @ExceptionHandler({SystemException.class})
    public RestResponse<Object> systemExceptionHandler(SystemException e,
                                                       HttpServletRequest request,
                                                       HttpServletResponse response) {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        if (!ServletUtils.isAjaxRequest(request)) {
            String message = e.getDefaultMessage();
            try {
                message = messageSource.getMessage(e.getCode(), e.getParameters(),
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(e.getCode(), e.getDefaultMessage())
                .formateLocalMessage(messageSource, e.getParameters());
    }

    @ExceptionHandler({NullPointerException.class})
    public RestResponse<Object> exceptionNullPointerException(NullPointerException e,
                                                              HttpServletRequest request,
                                                              HttpServletResponse response) {
        log.error("空指针异常", e);
        IResponseCode code = BaseResponseCode.GLOBAL_UNKOWN_ERROR;
        StackTraceElement[] stackTraces = e.getStackTrace();
        if (stackTraces != null) {
            StackTraceElement stackTrace = stackTraces[0];
            if (CharSequenceUtil.startWith(stackTrace.getClassName(), "com.zhg2yqq.wheels.")) {
                code = BaseResponseCode.RESOURCE_NOT_EXISTS;
            }
        }
        HttpStatus httpStatus = BaseResponseCode.RESOURCE_NOT_EXISTS.equals(code)
                ? HttpStatus.NOT_FOUND
                : HttpStatus.INTERNAL_SERVER_ERROR;
        response.setStatus(httpStatus.value());
        if (!ServletUtils.isAjaxRequest(request)) {
            String message = code.getDescription();
            try {
                message = messageSource.getMessage(code.getCode(), null,
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(code.getCode(), code.getDescription())
                .formateLocalMessage(messageSource);
    }

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public RestResponse<Object> exceptionMethodArgumentNotValidException(MethodArgumentNotValidException e,
                                                                         HttpServletRequest request,
                                                                         HttpServletResponse response) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        List<FieldError> fieldErrors = e.getFieldErrors();
        String message = fieldErrors.stream().map(fe -> fe.getField() + ":" + fe.getDefaultMessage()).collect(Collectors.joining(","));
        if (!ServletUtils.isAjaxRequest(request)) {
            try {
                message = messageSource.getMessage(BaseResponseCode.GLOBAL_PARAM_ERROR.getCode(), new Object[]{message},
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(BaseResponseCode.GLOBAL_PARAM_ERROR.getCode(), BaseResponseCode.GLOBAL_PARAM_ERROR.getDescription())
                .formateLocalMessage(messageSource, message);
    }

    @ExceptionHandler({NoHandlerFoundException.class})
    public RestResponse<Object> exceptionNullPointerException(NoHandlerFoundException e,
                                                              HttpServletRequest request,
                                                              HttpServletResponse response) {
        log.error("404", e);
        IResponseCode code = BaseResponseCode.RESOURCE_NOT_EXISTS;
        HttpStatus httpStatus = HttpStatus.NOT_FOUND;
        response.setStatus(httpStatus.value());
        if (!ServletUtils.isAjaxRequest(request)) {
            String message = code.getDescription();
            try {
                message = messageSource.getMessage(code.getCode(), null,
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(code.getCode(), code.getDescription())
                .formateLocalMessage(messageSource);
    }

    @ExceptionHandler({Exception.class})
    public RestResponse<Object> exceptionHandler(Exception e, HttpServletRequest request,
                                                 HttpServletResponse response) {
        log.error("系统错误", e);
        IResponseCode code = BaseResponseCode.GLOBAL_UNKOWN_ERROR;
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        if (!ServletUtils.isAjaxRequest(request)) {
            String message = code.getDescription();
            try {
                message = messageSource.getMessage(code.getCode(), null,
                        LocaleContextHolder.getLocale());
            } catch (NoSuchMessageException ex) {
            }
            request.setAttribute("errorMessage", message);
        }
        return RestResponse.buildWithMessage(code.getCode(), code.getDescription())
                .formateLocalMessage(messageSource);
    }
}
